package com.gsoe.problem.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.gsoe.base.enumeration.ProblemType;
import com.gsoe.base.exception.JudgeException;
import com.gsoe.base.exception.ProblemException;
import com.gsoe.base.model.MqMessage;
import com.gsoe.base.model.Result;
import com.gsoe.base.utils.UserUtil;
import com.gsoe.problem.client.JudgeClient;
import com.gsoe.problem.dto.JudgeExamDTO;
import com.gsoe.problem.dto.SaveJudgeTestDTO;
import com.gsoe.problem.mapper.ExamMapper;
import com.gsoe.problem.mapper.ExamProblemMapper;
import com.gsoe.problem.mapper.ExamRegisterUserMapper;
import com.gsoe.problem.mapper.TestProblemMapper;
import com.gsoe.problem.po.*;
import com.gsoe.problem.service.ExamProblemService;
import com.gsoe.problem.service.TestProblemService;
import com.gsoe.problem.service.TestService;
import com.gsoe.problem.vo.ExamProblemVO;
import com.gsoe.problem.vo.ProblemVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 考试题目中间表 服务实现类
 * </p>
 *
 * @author xiaoma
 * @since 2024-10-27
 */
@Service
@Slf4j
public class ExamProblemServiceImpl extends ServiceImpl<ExamProblemMapper, ExamProblem> implements ExamProblemService {
    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private ExamRegisterUserMapper examRegisterUserMapper;

    @Autowired
    private ExamProblemMapper examProblemMapper;

    @Autowired
    private TestProblemService testProblemService;

    @Autowired
    TestService testService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private JudgeClient judgeClient;

    @Autowired
    RedissonClient redissonClient;



    @Override
    public Result<ExamProblemVO> getExamProblemList(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if (exam == null) {
            throw new ProblemException("getExamProblemList:考试不存在");
        }
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            ExamProblemVO examProblemVO = new ExamProblemVO();
            examProblemVO.setRemainingTime(-1L);
            return Result.success(examProblemVO);
        }
        ExamRegisterUser examRegisterUser = examRegisterUserMapper.selectOne(new QueryWrapper<ExamRegisterUser>()
                .eq("exam_id", exam.getId())
                .eq("user_id", UserUtil.getLoginUser().getId())
        );
        if (examRegisterUser == null) {
            throw new ProblemException("未注册该场考试，请前往考试中心注册");
        }

        if (examRegisterUser.getStatus() >= 2) {
            throw new ProblemException("您已交卷，不能再进行考试");
        }

        List<Problem> problemListByExamId = examProblemMapper.getProblemListByExamId(examId,null);

        ExamProblemVO examProblemVO = null;
        String jsonString = stringRedisTemplate.opsForValue().get("examProblem:"+examId);
        if (jsonString != null) {
            try {
                examProblemVO = JSONUtil.toBean(jsonString, ExamProblemVO.class);
            }catch (Exception e){
                log.error("getExamProblemList:{}", e.getMessage());
            }
        }
        if(examProblemVO==null){
            RLock rLock = redissonClient.getReadWriteLock("examProblemLock:" + examId).readLock();
            try {
                rLock.lock();
                examProblemVO=new ExamProblemVO();
                Map<Integer, List<ProblemVO>> typeProblemVOListMapByProblemList = testProblemService.getTypeProblemVOListMapByProblemList(problemListByExamId);
                examProblemVO.setSingleChooseList(typeProblemVOListMapByProblemList.get(ProblemType.SINGLE));
                examProblemVO.setBigList(typeProblemVOListMapByProblemList.get(ProblemType.BIG));
                examProblemVO.setFillList(typeProblemVOListMapByProblemList.get(ProblemType.FILL));
                examProblemVO.setJudgeList(typeProblemVOListMapByProblemList.get(ProblemType.JUDGE));
                examProblemVO.setMultipleChooseList(typeProblemVOListMapByProblemList.get(ProblemType.MULTIPLE));
                stringRedisTemplate.opsForValue().set("examProblem:"+examId, JSONUtil.toJsonStr(examProblemVO),exam.getValidity()/10, TimeUnit.MINUTES);
            }catch (Exception e){
                log.error("getExamProblemList,{}",e.getMessage());
            }finally {
                rLock.unlock();
            }


        }
        examProblemVO.setRemainingTime(Duration.between(LocalDateTime.now(), exam.getEndTime()).getSeconds());
        Result<Map<Long, String>> examCache;
        try {
            examCache = judgeClient.getExamCache(examId, UserUtil.getLoginUser().getId());
            if (examCache.getCode() != 200) throw new RuntimeException(examCache.getMsg());
        } catch (Exception e) {
            log.error(e.getMessage());
            examProblemVO.setCache(new HashMap<>());
            Result<ExamProblemVO> result = new Result<>();
            result.setMsg("获取缓存失败，但仍尝试进入考试，如果有做题记录建议刷新重试");
            result.setCode(510);
            result.setData(examProblemVO);
            return result;
        }

        examProblemVO.setCache(examCache.getData());
        return Result.success(examProblemVO);
    }

    @Override
    public void autoJudge(Long examId) {
        //不需要人工校验的集合
        List<Problem>noNeedPeopleJudgeProblemList=examProblemMapper.getProblemListByExamId(examId,0);
        //需要人工校验的集合
        List<Problem>needPeopleJudgeProblemList=examProblemMapper.getProblemListByExamId(examId,1);

        if(CollectionUtil.isEmpty(noNeedPeopleJudgeProblemList) && CollectionUtil.isEmpty(needPeopleJudgeProblemList)){
            throw new ProblemException("该考试无任何题目，请校验考试信息");
        }

        List<ExamRegisterUser> examRegisterUsers = examRegisterUserMapper.selectList(new QueryWrapper<ExamRegisterUser>()
                .eq("exam_id", examId));
        List<Long> registerUserIdList = examRegisterUsers
                .stream().map(ExamRegisterUser::getUserId).toList();
        //无注册用户
        if (CollectionUtil.isEmpty(registerUserIdList))return;
        //1、examId
        //2、userList
        MqMessage mqMessage=new MqMessage();
        mqMessage.setData1(examId);
        mqMessage.setData2(registerUserIdList);
        //拿取答题结果  userId ProblemId input
        Map<Long, Map<Long, String>> userIdProblemIdInputMap;
        try {
            Result<Map<Long, Map<Long, String>>> examCacheByUserListResult= judgeClient.getExamCacheByUserList(mqMessage);
            if (examCacheByUserListResult.getCode() != 200) throw new RuntimeException(examCacheByUserListResult.getMsg());
            userIdProblemIdInputMap=examCacheByUserListResult.getData();
        }catch (Exception e){
            throw new JudgeException("autoJudge:"+e.getMessage());
        }
        //无答题记录
        if(CollectionUtil.isEmpty(userIdProblemIdInputMap)){
            return;
        }
        CheckAnswer checkAnswer=new CheckAnswer();
        checkAnswer.setSingleScore(examProblemMapper.getScore(examId,1));
        checkAnswer.setFillScore(examProblemMapper.getScore(examId,2));
        checkAnswer.setJudgeScore(examProblemMapper.getScore(examId,3));
        checkAnswer.setMultiScore(examProblemMapper.getScore(examId,4));
        checkAnswer.setBigScore(examProblemMapper.getScore(examId,5));
        checkAnswer.setProblemList(noNeedPeopleJudgeProblemList);

        List<JudgeExamDTO> judgeExamDTOList=new ArrayList<>();

        examRegisterUsers.forEach(registerUser->{
            try {
                Integer i=examRegisterUserMapper.updateStatusWithJudging(examId,registerUser.getUserId(),3);
                if(i>0){
                    Map<Long, String> problemIdInputMap = userIdProblemIdInputMap.get(registerUser.getUserId());
                    if(!CollectionUtil.isEmpty(problemIdInputMap)){
                        checkAnswer.setProblemIDAndInput(problemIdInputMap);
                        //1：总分
                        //2：细分
                        //3: 输入
                        SaveJudgeTestDTO saveJudgeTestDTO = testService.getScoreWithCheckAnswer(checkAnswer);

                        Map<Long,Integer>problemIdWithScore= saveJudgeTestDTO.getProblemIdWithScore();
                        Map<Long,String>problemIdWithInput= saveJudgeTestDTO.getProblemIdWithInput();

                        problemIdWithScore.forEach((problemId,score)->{
                            JudgeExamDTO judgeExamDTO=new JudgeExamDTO();
                            judgeExamDTO.setProblemId(problemId);
                            judgeExamDTO.setExamId(examId);
                            judgeExamDTO.setUserId(registerUser.getUserId());
                            judgeExamDTO.setInput(problemIdWithInput.get(problemId));
                            judgeExamDTO.setUserName(registerUser.getUserName());
                            judgeExamDTO.setScore(score);
                            judgeExamDTOList.add(judgeExamDTO);
                        });

                        needPeopleJudgeProblemList.forEach(problem -> {
                            JudgeExamDTO judgeExamDTO=new JudgeExamDTO();
                            judgeExamDTO.setProblemId(problem.getId());
                            judgeExamDTO.setExamId(examId);
                            judgeExamDTO.setUserId(registerUser.getUserId());
                            judgeExamDTO.setUserName(registerUser.getUserName());
                            judgeExamDTO.setInput(userIdProblemIdInputMap.get(registerUser.getUserId()).get(problem.getId()));
                            judgeExamDTOList.add(judgeExamDTO);
                        });

                        rabbitTemplate.convertAndSend("judge.direct","saveJudgeExam", judgeExamDTOList);
                        registerUser.setScore(saveJudgeTestDTO.getScore());
                    }
                    registerUser.setStatus(4);
                    examRegisterUserMapper.updateById(registerUser);
                }
            }catch (Exception e){
                registerUser.setStatus(5);
                examRegisterUserMapper.updateById(registerUser);
                throw new JudgeException("autoJudge:"+e.getMessage());
            }
        });
    }
}
